/*
	setup_PIT_64.c

	provides functions for calibrating the PIT

	Author: Aidan Goddard, 14/5/13
*/

#include "../kernel/headers/types.h"

/*
	PIT_Setup

	param 1 - desired frequency

	returns actual frequency

	Calculates the closest possible frequency to the desired
	one that the PIT can be set to. Then calibrates the PIT to
	this frequency in repeated mode.
*/
double PIT_Setup(double required_frequency)
{
	// calculate the frequency divider. (this will give an approximation to the required frequency value)
	double PIT_frequency = 1193182.0;
	if(required_frequency < 20)
		required_frequency = 20;
	double d_divider = PIT_frequency/required_frequency;

	unsigned short divider_low = (unsigned short)d_divider;
	unsigned short divider_high = (unsigned short)d_divider;

	// to make sure it doesn't end up dividing by 0
	if(divider_high != 0xffff)
	{
		divider_high += 1;
	}

	// calculate which approximation is closer
	double actual_frequency_high = PIT_frequency / divider_low;
	double actual_frequency_low = PIT_frequency / divider_high;

	// get the difference between the 2 calculated frequencies and the required frequency
	actual_frequency_high -= required_frequency;
	actual_frequency_low -= required_frequency;
	actual_frequency_low *= -1;

	// which one is smaller?
	unsigned short divider = 0;
	double actual_frequency = required_frequency;
	if(actual_frequency_low < actual_frequency_high)
	{
		actual_frequency -= actual_frequency_low;
		divider = divider_high;
	}
	else
	{
		actual_frequency += actual_frequency_high;
		divider = divider_low;
	}

	// program the PIT
	outportb(0x43, 0x34);	// send command (channel 0, lobyte/hibyte, mode 2, binary)
	outportb(0x40, (unsigned char)(divider & 0x00ff));	// send low byte of divider to data port
	outportb(0x40, (unsigned char)(divider >> 8));		// send high byte of divider to data port

	// finished, return the generated frequency
	return actual_frequency;
}
